XplentyでFirestoreのAPIを叩いてデータを取得する
はじめに
データアナリティクス事業本部のkobayashiです。
XplentyはETL、ELTツールとして様々なデータソースを扱え、また処理もXplentyのGUIで直感的に作成できます。 今回もXplentyを使ってAPIを叩いてデータを取しそのデータをDBに保存してみます。
Xplenty | Simplified ETL & ELT to BigQuery, Snowflake, Redshift & Azure
前回はレスポンスがそれほど複雑でないAPIとしてSlack Web APIを使ってチャンネル一覧を取得しました。今回は少しだけ複雑なレスポンスを返すAPIのデータをXplentyで取り扱ってみたいと思います。
題材は何にしようかと考えていたのですが、趣味で個人的にReactとFirebaseを使ってアプリを作っておりデータベースとしてFirestoreを使っていますが、Firestoreはデータを操作するAPIが用意されているのを思い出したのでここからデータを取得してRedshiftへ保存してみたいと思います。(FirestoreならBigQueryへエクスポートすればいいじゃんというのは置いておいて)
前回の記事
Firestoreの設定
FiresoteのAPIはCloud Firestore API | Firebase にドキュメントがあり詳しく書かれていますのでご確認ください。またFirestoreにXplentyから接続するためにルールを定義する必要がありますが、今回は簡単にReadはどこからでもアクセスできるようにしてあります。
以下が今回取得するFirestoreのドキュメントの中身になります。
単純なユーザー情報になりますが、一点hobby
がarray
型になっておりこのままだとうまくRedshiftへデータを保存できないので、hobby
のみ別テーブルに切り出して保存したいと思います。
Firestore apiのレスポンス
{ "name": "projects/fir-cm-kbm-test/databases/(default)/documents/sample-dataset/42iLM001obPwloaJ7tQC", "fields": { "age": { "integerValue": "20" }, "mail": { "stringValue": "kenji@example.com" }, "username": { "stringValue": "ケンジ" }, "hobby": { "arrayValue": { "values": [ { "stringValue": "読書" }, { "stringValue": "登山" }, { "stringValue": "ウォーキング" }, { "stringValue": "渓流釣り" }, { "stringValue": "ロードバイク" }, { "stringValue": "BBQ" } ] } } }, "createTime": "2020-06-25T02:30:28.534715Z", "updateTime": "2020-06-26T19:30:13.506135Z" }
キー:データという形でなく型を挟むのでデータの取り扱いに注意します。また階層が深くなるのでXplentyの関数JsonStringToMap
関数をネストして使います。
Xplentyのパッケージの作成
Xplentyのパッケージの中身は大まかに以下になります。
- Firestore APIにリクエストを送りレスポンスを取得する
- 受け取ったデータを加工してRedshiftへ保存できる形に加工する
- データをユーザーテーブル(
fs_user
)と趣味テーブル(fs_hobby
)用に分ける - Redshiftへユーザーテーブル(
fs_user
)と趣味テーブル(fs_hobby
)を作成しデータを登録する
これらのコンポーネントを組み合わせてパッケージを作成していきます。
はじめにサイドバーからPackages
を選択してNew Package
を押下し新しいパッケージを作成します。
1.Firestore API用にREST APIコンポーネントを作成
まずはFirestoreのAPIを使いドキュメントの情報を取得し、加工・保存を行います。
少しだけFirestoreのAPIの話になるのですが、Firestoreドキュメントを取得するので本来ならFirestore APIのDocument
を使えばいいのですが、XplentyのPaginationがFirestoreのPatinationに対応していないためDocument
だと全件取得できません。したがってrunQuery
を使います。
手順1-1).Add componet
を押下し、SourcesでREST API
を選択する
手順1-2).作成されたアイコンをダブルクリックするとREST APIコンポーネント設定用のモーダルが表示されるので、Authentication
でNone
を選択する。
手順1-3).Request and response
の設定を行いNext
を押下する。
- Method :
POST
を選択 - URL :
https://firestore.googleapis.com/v1/projects/{プロジェクト名}/databases/(default)/documents:runQuery
を入力 -
Headers
- Key :
Content-Type
を入力 - VALUE :
application/json
を入力
- Key :
- Body :
{ "structuredQuery": { "select": { "fields": [ {"fieldPath": "username"}, {"fieldPath": "mail"}, {"fieldPath": "age"}, {"fieldPath": "hobby"}, ] }, "from": [ {"collectionId": "sample-dataset"} ] } }
- Use Pagination : チェックしない
他の設定値はデフォルトのままにします。
手順1-4).Response Schema
へ進み、Available Fieldsでfield
の右のプラスをクリックしSelected Fields
でfield
が選択された状態にする。その後Save
を押下し、設定を保存する。
この画面ではレスポンスの内容を確認できるので必要なデータを確認しながら設定を行えます。
これでREST APIコンポーネントの設定は終わりますので、次にデータ加工用のコンポーネントをを設定します。
2.データ加工用のコンポーネント作成
Slack Web APIと同じ作りになります。Selectコンポーネントを2つ繋げて、1つ目のコンポーネントでレスポンスをフラット化し、2つ目のコンポーネントでJson文字列を扱う関数を駆使して欲しい形に加工します。
手順2-1).Add componet
を押下し、TransformationsでSelect
を選択する。この操作を2回行い2つSelectコンポーネントを作成する。
手順2-2).作成したSelectコンポーネントを前段のREST APIコンポーネントと接続する。(接続していないとカラムのマッピングができないため)
手順2-3).作成したSelectコンポーネントの1つ目を選択して設定を行いSave
を押下し保存する。
- EXPRESSION :
Flatten(field)
を入力 - ALIAS :
field
を入力
手順2-4).作成したSelectコンポーネントの2つ目を選択して設定を行いSave
を押下し保存する。
- EXPRESSION、ALIASに以下のように入力
EXPRESSION | ALIAS |
---|---|
JsonStringToMap(JsonStringToMap(field)#'document')#'name' | d_name |
ToDate (JsonStringToMap(JsonStringToMap(field)#'document')#'createTime') | d_createTime |
ToDate (JsonStringToMap(JsonStringToMap(field)#'document')#'updateTime') | d_updateTime |
JsonStringToMap(JsonExtract(JsonStringToMap(field)#'document','$.fields.username'))#'stringValue' | username |
JsonStringToMap(JsonExtract(JsonStringToMap(field)#'document','$.fields.mail'))#'stringValue' | |
ParseIntOrDefault(JsonStringToMap(JsonExtract(JsonStringToMap(field)#'document','$.fields.age'))#'integerValue',null) | age |
JsonStringToMap(JsonExtract(JsonStringToMap(field)#'document','$.fields.hobby'))#'arrayValue' | hobby |
d_name
はユーザーテーブルのキーにあたります。
これでRedshiftへ保存する元データの加工は終わりになります。
3.データの分離
ここまでの加工ではユーザーデータの中に趣味テーブル用のデータがJson文字列として含まれてしまっている状態ですのでこれを分離してユーザーテーブル、趣味テーブル用にデータを作成します。
Xplentyではこの様に1つのデータから複数ディスティネーションを作りたい場合はCloneコンポーネントを使えば良いです。
手順3-1).Add componet
を押下し、TransformationsでClone
を選択してCloneコンポーネントを作成しSlectコンポーネントと接続する
ここでhobby用のデータはjson文字列のデータになっています。これは(手順2)を始める前とデータの状態と同じ様な状態なので(手順2)と同じ手順でデータを加工します。
手順3-2).Add componet
を押下し、TransformationsでSelect
を選択する。この操作を2回行い2つSelectコンポーネントを作成する。
手順3-3).作成したSelectコンポーネントを前段のCloneと接続する
手順2-3).作成したSelectコンポーネントの1つ目を選択して設定を行いSave
を押下し保存する。
- EXPRESSION、ALIASに以下のように入力
EXPRESSION | ALIAS |
---|---|
d_name | d_name |
Flatten(JsonStringToBag(JsonStringToMap(hobby)#'values')) | hobby |
ユーザーデータのキーに当たるものも渡してこれを条件にユーザーテーブルと趣味テーブルを結合したいので残しておきます。
手順2-4).作成したSelectコンポーネントの2つ目を選択して設定を行いSave
を押下し保存する。
- EXPRESSION、ALIASに以下のように入力
EXPRESSION | ALIAS |
---|---|
d_name | d_name |
JsonStringToMap(hobby)#'stringValue' | hobby |
これでユーザーデータと趣味データが分離でき、Redshiftへ保存する形へデータを変換できましたので、最後にRedshiftへ保存する設定をします。
4.保存先としてRedshiftコンポーネント作成
Slack Web APIのときと同様に簡単に行うためテーブルは予め作成せずXplentyのジョブで自動作成し、データは毎回洗い替えを行う設定にします。
手順4-1).Add componet
を押下し、DestinationsでAmazon Redshift
を選択する
手順4-2).画面上にRedshiftのアイコンが作成されるので先に作成したSelectコンポーネントのアイコンとDrag&Dropで紐付ける。
手順4-3).RedshiftのアイコンをダブルクリックするとRedshiftコンポーネント設定用のモーダルが表示されるので、設定済みのRedshiftのconnectionを選択する。
手順4-4).移行先のRedshiftのスキーマとテーブルを入力し各種設定を行い、Next
を押下する。
- Automatically create table if it doesn't exist : チェックする
- Automatically add missing columns : チェックする
- Operation type :
Overwrite (Truncate and insert)
を選択する
他の設定はデフォルトで問題ありません。
手順4-5).移行元と移行先のカラムマッピングを行う。移行先のテーブルは新規作成なのでAuto fill
を押下して自動設定を行う。
手順4-6).右下のSave
を押下して設定を保存する。
手順4-7).(手順4-1〜4-6)を繰り返して趣味テーブル(fs_hobby
)の設定を行う
これでパッケージの作成は完了です。
Xplentyのパッケージの実行と確認
実際にジョブを実行して結果を確認します。
Redshitでデータを確認
ユーザーテーブル
select * from cm_kobayashi_test.fs_user;
d_name | d_createtime | d_updatetime | username | age | |
---|---|---|---|---|---|
rs_42iLM001obPwloaJ7tQC | 2020-06-25 02:30:28.534000 | 2020-06-26 19:30:13.506000 | ケンジ | kenji@example.com | 20 |
rs_96e9QOLYdI9VAgaGh4nA | 2020-06-25 02:32:14.491000 | 2020-06-25 04:02:59.448000 | ハナコ | hanako@example.com | 18 |
rs_EcsnAPIbAHEloM5ofn5s | 2020-06-25 02:31:11.174000 | 2020-06-25 04:03:22.915000 | タロウ | taro@example.com | 19 |
Redshitでデータを確認
趣味テーブル
select * from cm_kobayashi_test.fs_hobby;
d_name | hobby |
---|---|
rs_42iLM001obPwloaJ7tQC | 読書 |
rs_42iLM001obPwloaJ7tQC | 登山 |
rs_42iLM001obPwloaJ7tQC | ウォーキング |
rs_42iLM001obPwloaJ7tQC | 渓流釣り |
rs_42iLM001obPwloaJ7tQC | ロードゲーム |
rs_42iLM001obPwloaJ7tQC | BBQ |
rs_96e9QOLYdI9VAgaGh4nA | 筋トレ |
rs_96e9QOLYdI9VAgaGh4nA | ヨガ |
rs_96e9QOLYdI9VAgaGh4nA | 温泉巡り |
rs_EcsnAPIbAHEloM5ofn5s | 温泉巡り |
rs_EcsnAPIbAHEloM5ofn5s | ロードゲーム |
この様な形でうまくユーザーテーブルと趣味テーブルにデータを分離して登録できました。
まとめ
XplentyのREST APIコンポーネントを使ってFirestore APIからデータを取得してみました。1つのレスポンスから親子関係となるテーブルを切り出す場合については次回のエントリでもう少し複雑なレスポンスの事例を記事にまとめたいと思います。
最後まで読んで頂いてありがとうございました。